package software

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/gogf/gf/v2/frame/g"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// mongodb://root:root@localhost:27017/?authSource=admin&compressors=snappy,zlib,zstd

func NewMongodb(ctx context.Context, collection string) *mongodbHelper {
	uri := g.Config().MustGet(ctx, "software.mongodb.uri", "").String()
	database := g.Config().MustGet(ctx, "software.mongodb.database", "").String()
	username := g.Config().MustGet(ctx, "software.mongodb.username", "").String()
	password := g.Config().MustGet(ctx, "software.mongodb.password", "").String()

	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	opts := options.Client().
		ApplyURI(uri).
		SetAuth(options.Credential{
			Username:   username,
			Password:   password,
			AuthSource: "admin",
		}).
		SetCompressors([]string{"snappy", "zlib", "zstd"})
	client, err := mongo.Connect(ctx, opts)
	if err != nil {
		log.Fatal(err)
	}
	//defer func() {
	//	if err = client.Disconnect(ctx); err != nil {
	//		panic(err)
	//	}
	//}()

	// err = client.Ping(ctx, readpref.Primary())

	db := client.Database(database)
	collect := db.Collection(collection)

	return &mongodbHelper{
		uri:        uri,
		client:     client,
		database:   database,
		db:         db,
		collection: collection,
		collect:    collect,
	}
}

type mongodbHelper struct {
	uri        string
	client     *mongo.Client
	database   string
	db         *mongo.Database
	collection string
	collect    *mongo.Collection
}

func (r *mongodbHelper) Find(filter interface{}, results *[]bson.D) {
	ctx := context.TODO()

	cur, err := r.collect.Find(ctx, filter)
	if err != nil {
		log.Fatal(err)
	}
	defer func(cur *mongo.Cursor, ctx context.Context) {
		err := cur.Close(ctx)
		if err != nil {
			panic(err)
		}
	}(cur, ctx)

	for cur.Next(ctx) {
		var result bson.D
		err := cur.Decode(&result)
		if err != nil {
			log.Fatal(err)
		}

		*results = append(*results, result)
	}
	if err := cur.Err(); err != nil {
		log.Fatal(err)
	}
}
func (r *mongodbHelper) FindOne(filter, result interface{}) {
	err := r.collect.FindOne(context.TODO(), filter).Decode(result)
	if err == mongo.ErrNoDocuments {
		fmt.Println("record does not exist")
	} else if err != nil {
		log.Fatal(err)
	}
}
func (r *mongodbHelper) InsertOne(data interface{}) interface{} {
	res, err := r.collect.InsertOne(context.TODO(), data)
	if err != nil {
		log.Fatal(err)
	}

	return res.InsertedID
}
func (r *mongodbHelper) UpdateOne(filter, update interface{}) (int64, int64) {
	result, err := r.collect.UpdateOne(context.TODO(), filter, update)
	if err != nil {
		log.Fatal(err)
	}

	return result.MatchedCount, result.ModifiedCount
}
func (r *mongodbHelper) DeleteOne(filter interface{}) int64 {
	result, err := r.collect.DeleteOne(context.TODO(), filter)
	if err != nil {
		log.Fatal(err)
	}

	return result.DeletedCount
}
func (r *mongodbHelper) ListDatabaseNames() []string {
	databases, err := r.client.ListDatabaseNames(context.TODO(), bson.M{})
	if err != nil {
		log.Fatalf("Failed to list databases: %v", err)
	}

	return databases
}
func (r *mongodbHelper) ListCollectionNames() []string {
	collections, err := r.db.ListCollectionNames(context.TODO(), bson.M{})
	if err != nil {
		log.Fatalf("Failed to list collections in database %v", err)
	}

	return collections
}
